package com.hxzy.common.aspect;


import com.hxzy.common.annon.RateLimiter;
import com.hxzy.common.enums.LimitType;
import com.hxzy.common.util.IpUtils;
import com.hxzy.common.util.ServletUtils;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Objects;

/**
 * redis限流的增强
 */
@Aspect
@Component
@Log4j2
public class RedisRateLimiterAspect {

    @Pointcut(value = "@annotation(com.hxzy.common.annon.RateLimiter)")
    private void rateLimiterPointCut(){
    }

    /**
     * redis执行lua脚本对象
     */
    @Autowired
    private RedisScript<Long> limitScript;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * redis限流
     * @param jp
     * @param limiter
     */
    @Before(value ="rateLimiterPointCut() && @annotation(limiter)" )
    public void beforeLimit(JoinPoint jp, RateLimiter limiter){

        String redisKey=limiter.key()+jp.getSignature().toShortString();

        if(limiter.limitType()== LimitType.IP){
            redisKey+="-"+ IpUtils.getIpAddr(ServletUtils.getRequest());
        }

        //限流次数
        int  count= limiter.count();
        //时间
        int  time= limiter.time();

        //执行限流脚本
        try
        {
            Long number = (Long) redisTemplate.execute(limitScript, Arrays.asList(redisKey), count, time);

            if (Objects.isNull(number) || number.intValue() > count)
            {
                throw new RuntimeException("访问过于频繁，请稍候再试");
            }
            log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), redisKey);
        }
        catch (Exception e)
        {
            throw new RuntimeException("服务器限流异常，请稍候再试");
        }
    }


}
